/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package pagoda.util.scripting;

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import org.w3c.dom.Attr;
import org.w3c.dom.Node;
import pagoda.util.StringUtil;
import pagoda.util.exceptions.PagodaException;
import pagoda.util.xml.PositionalXMLReader;

/**
 *
 * @author Kevin Krohe
 */
public class ExpressionUtil {

    public static <V> Expression<V> build(
            final ScriptEngine engine,
            final String source,
            final Bindings bindings
            )
    {
        if(engine instanceof Compilable)
        {
            try {
                final CompiledScript cs = ((Compilable) engine).compile(source);

                return new Expression<V>() {

                    public V eval() {
                        try
                        {
                            Object o = cs.eval(bindings);
                            return (V)o;
                        }
                        catch(Throwable t)
                        {
                            throw new PagodaException(
                                    t,
                                    "Exception thrown during script invocation\n" +
                                    "Message:\t%s\n",
                                    t.toString()
                                    );
                        }
                    }
                };

            } catch (Throwable t) {
                throw new PagodaException(
                        t,
                        "Exception thrown during script compilation\n" +
                        "Message:\t%s\n",
                        t.toString()
                        );
            }

        }

        return new Expression<V>() {

            public V eval() {
                try
                {
                    Object o = engine.eval(source, bindings);
                    return (V)o;
                }
                catch(Throwable t)
                {
                    throw new PagodaException(
                            t,
                            "Exception thrown during script invocation\n" +
                            "Message:\t%s\n",
                            t.toString()
                            );
                }
            }
        };
    }

    public static <V> Expression<V> build(
            final ScriptEngine engine,
            final Node node,
            final Bindings bindings
            )
    {
        if(node == null)
            return null;

        final String source;

        if(node instanceof Attr)
            source = node.getNodeValue();
        else
            source = node.getTextContent();
        
        final String ln = String.valueOf(
                node.getUserData(
                    PositionalXMLReader.LINE_NUMBER_KEY_NAME
                    )
                );
        final String fn = String.valueOf(
                node.getUserData(
                    PositionalXMLReader.LINE_NUMBER_KEY_NAME
                    )
                );


        if(engine instanceof Compilable)
        {
            try {
                final CompiledScript cs = ((Compilable) engine).compile(source);

                return new Expression<V>() {

                    public V eval() {
                        try
                        {
                            Object o = cs.eval(bindings);
                            return (V)o;
                        }
                        catch(Throwable t)
                        {
                            throw new PagodaException(
                                t,
                                "Exception thrown during script invocation\n" +
                                "FileName:\t%s\n" +
                                "Linenumber:\t%s\n" +
                                "Message:\t%s\n",
                                StringUtil.IfNullOrEmpty(fn, "Unknown"),
                                ln,
                                t.toString()
                                );
                        }
                    }
                };

            } catch (Throwable t) {
                throw new PagodaException(
                        t,
                        "Exception thrown during script compilation\n" +
                        "Message:\t%s\n",
                        t.toString()
                        );
            }        }


        return new Expression<V>() {

            public V eval() {
                try
                {
                    Object o = engine.eval(source, bindings);
                    return (V)o;
                }
                catch(Throwable t)
                {
                    throw new PagodaException(
                            t,
                            "Exception thrown during script invocation\n" +
                            "FileName:\t%s\n" +
                            "Linenumber:\t%s\n" +
                            "Message:\t%s\n",
                            StringUtil.IfNullOrEmpty(fn, "Unknown"),
                            ln,
                            t.toString()
                            );
                }
            }
        };
    }

    private static ScriptEngine _defaultEngine;

    public static ScriptEngine getEngineByName(String name)
    {
        ScriptEngineManager sem = new ScriptEngineManager();
        return sem.getEngineByName(name);
    }

    public static synchronized ScriptEngine getDefaultScriptEngine()
    {
        if(_defaultEngine == null)
            _defaultEngine = getEngineByName("JavaScript");
        return _defaultEngine;
    }
}
